import type { Ref } from 'vue'
import { isRef, ref, toRaw, unref } from 'vue'

type Error<T> = Record<keyof T, { status: boolean, text: string }>
interface Target<T> { name: keyof T, value?: any }
interface FormParams<T = any> {
  initValue: T
  validator: Partial<Record<keyof T, any>>
  onSubmit: (params: T) => any
  onError?: (error: any) => any
}

interface FormReturn<T = any> {
  handleSubmit: () => void
  handleInput: (e: Event) => void
  handleBlur: (e: Event) => void
  resetForm: () => void
  setFieldValue: (name: keyof T, value: any) => void
  setFieldValues: (value: any) => void
  validatorField: (key: keyof T) => Promise<any>
  validatorFields: () => Promise<any>
  values: Ref<T>
  errors: Error<T>
}
export function useForm<T = Record<string, any>>({
  initValue,
  validator,
  onSubmit,
  onError,
}: FormParams<T>): FormReturn<T> {
  const initValue_backup = { ...unref(initValue) }
  const values = isRef(initValue) ? initValue : ref<T>(initValue)
  const errors = ref<Error<T>>({} as Error<T>)
  const validatorField = (key: keyof T) => {
    return new Promise((resolve, reject) => {
      const error = validator[key](values.value[key])
      if (error) {
        errors.value[key] = { text: '', status: true }
        reject({ [key]: error })
      }
      else {
        resolve(values.value[key])
      }
    })
  }
  const validatorFields = () => {
    const arr = (Object.keys(validator) as any).map((key: keyof T) =>
      validatorField(key),
    )
    return Promise.all(arr)
  }
  const handleSubmit = () => {
    validatorFields()
      .then(() => {
        onSubmit(toRaw(values.value))
      })
      .catch((e) => {
        console.error(e)
        onError?.(e)
      })
  }
  const handleBlur = (e: Event) => {
    const { name } = e.target as unknown as Target<T>
    if (validator?.[name]) {
      const error = validator[name](values.value[name])
      if (error) {
        errors.value[name] = { ...(errors.value[name] || {}), status: true }
      }
    }
  }
  const handleInput = (e: Event) => {
    const { name, value } = e.target as unknown as Target<T>
    if (validator?.[name]) {
      const error = validator[name](value)
      if (!error) {
        errors.value[name] = { text: '', status: false }
      }
      else {
        errors.value[name] = { text: value ? error : '', status: true }
      }
    }
  }
  const setFieldValue = (name: keyof T, value: any) => {
    values.value[name] = value
  }
  const setFieldValues = (value: any) => {
    values.value = value
  }
  const clearErrors = () => {
    errors.value = {}
  }
  const resetForm = () => {
    values.value = initValue_backup
    errors.value = {}
  }
  return {
    handleSubmit,
    handleInput,
    handleBlur,
    resetForm,
    setFieldValue,
    setFieldValues,
    validatorField,
    validatorFields,
    clearErrors,
    values,
    errors,
  } as FormReturn<T>
}
